package com.glsx.core.repository.specs;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;


public class CustomerSpecs {
	@SuppressWarnings("unchecked")
	public static <T> Specification<T> byAuto(final EntityManager entityManager, final T examle){
		final Class<T> type = (Class<T>) examle.getClass();
		return new Specification<T>(){
			@Override
			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> predicates = new ArrayList<>();
				EntityType<T> entityType = entityManager.getMetamodel().entity(type);
				
				for(Attribute<T, ?> attr:entityType.getDeclaredAttributes()){
					Object attrValue = getValue(examle, attr);
					if(null != attrValue){
						if(attr.getJavaType() == String.class){
							if(!StringUtils.isEmpty(attrValue)){
								predicates.add(cb.like(root.get(attribute(entityType, attr.getName(), String.class)), pattern((String) attrValue)));
							}else{
								predicates.add(cb.equal(root.get(attribute(entityType, attr.getName(), attrValue.getClass())),attrValue));
							}
						}
					}
				}
				return predicates.isEmpty()?cb.conjunction():cb.and(toArray(predicates, Predicate.class));
			}
			private Predicate[] toArray(List<Predicate> predicates, Class<Predicate> predicate) {
				Predicate[] p = new Predicate[predicates.size()];
				return predicates.toArray(p);
			}
			@SuppressWarnings("hiding")
			private <T> Object getValue(T example, Attribute<T, ?> attr){
				return ReflectionUtils.getField((Field)attr.getJavaMember(), examle);
			}
			@SuppressWarnings("hiding")
			private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entityType, String fieldName, Class<E> fieldClass){
				return entityType.getDeclaredSingularAttribute(fieldName, fieldClass);
			}
		};
			
	}
		
	private static String pattern(String str){
		return str+"%";
	}
	
}
